﻿#include <QDir>
#include <QDir>
#include <QMenu>
#include <QMessageBox>
#include <Resources.h>
#include <FileSystemHandler.h>
#include "FileSystemTreeWidget.h"
#include "FileSystemTreeWidgetItem.h"

namespace Domy {
/**
 * @brief FileSystemTreeWidget
 */
FileSystemTreeWidget::FileSystemTreeWidget(QWidget *parent)
    : QTreeWidget(parent),
      m_current_action(0),
      m_provider(new FileSystemDisplayProvider())
{
    this->setContextMenuPolicy(Qt::CustomContextMenu);

    this->setEditTriggers(QAbstractItemView::EditKeyPressed);

    connect(this, SIGNAL(itemExpanded(QTreeWidgetItem *)), this, SLOT(slotOnItemExpanded(QTreeWidgetItem *)));
    connect(this, SIGNAL(itemCollapsed(QTreeWidgetItem *)), this, SLOT(slotOnItemCollapsed(QTreeWidgetItem *)));
    connect(this, SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), this, SLOT(slotOnItemDoubleClicked(QTreeWidgetItem *, int)));
    connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(slotOnCustomContextMenuRequested(const QPoint &)));

    addFinished();
}

FileSystemTreeWidget::~FileSystemTreeWidget()
{
    delete m_provider;
    m_provider = Q_NULLPTR;
}

void FileSystemTreeWidget::add(const QFileInfo &fi)
{
    FileSystemItem *item = Q_NULLPTR;
    if (fi.isDir() || fi.isRoot()) {
        item = new DirItem(m_provider);
    }
    else if (fi.isFile()) {
        item = new FileItem(m_provider);
    }
    else {
        return;
    }

    item->setFileInfo(fi);
    this->addTopLevelItem(item);
}

void FileSystemTreeWidget::add(const QFileInfoList &fi_list)
{
    for (const QFileInfo &fi : fi_list) {
        add(fi);
    }
}

void FileSystemTreeWidget::setHeaderContent(const QString &text, const QIcon &icon)
{
    QTreeWidgetItem *header_item = this->getHeaderItem();
    header_item->setIcon(0, icon);
    header_item->setText(0, text);
}

void FileSystemTreeWidget::setHeaderText(const QString &text)
{
    QTreeWidgetItem *header_item = this->getHeaderItem();
    header_item->setText(0, text);
}

void FileSystemTreeWidget::setHeaderIcon(const QIcon &icon)
{
    QTreeWidgetItem *header_item = this->getHeaderItem();
    header_item->setIcon(0, icon);
}

void FileSystemTreeWidget::slotOnItemExpanded(QTreeWidgetItem *item)
{
    DirItem *dir_item = dynamic_cast<DirItem *>(item);
    if (dir_item) {
        dir_item->expand();
    }
}

void FileSystemTreeWidget::slotOnItemCollapsed(QTreeWidgetItem *item)
{
    DirItem *dir_item = dynamic_cast<DirItem *>(item);
    if (dir_item) {
        dir_item->collapse();
    }
}

void FileSystemTreeWidget::slotOnItemDoubleClicked(QTreeWidgetItem *item, int column)
{
    if (column != 0) {
        return;
    }

    FileItem *file_item = dynamic_cast<FileItem *>(item);
    if (file_item) {
        emit signalOpenFile(file_item->getFileInfo().absoluteFilePath());
    }
}

void FileSystemTreeWidget::slotOnItemChanged(QTreeWidgetItem *item, int column)
{
    Q_UNUSED(column)

    FileSystemItem *file_item = dynamic_cast<FileSystemItem *>(item);
    if (!file_item) {
        return;
    }

    if (file_item->getName() == file_item->text(0)
            && !file_item->getName().isEmpty()) {
        return;
    }

    switch (m_current_action) {
    case FileSystemMenuManager::FileAct_Rename:
    case FileSystemMenuManager::DirAct_Rename:
    {
        QFileInfo old_file = file_item->getFileInfo();
        QFileInfo new_file;

        if (FileSystemHandler::rename(old_file, file_item->text(0), new_file)) {
            file_item->setFileInfo(new_file);
        }
        else {
            QMessageBox::critical(this, tr("Rename"), tr("The file(\"%1\") renamed unsuccessfully !").arg(old_file.absoluteFilePath()),
                                  QMessageBox::Ok, QMessageBox::Ok);

            file_item->setText(0, old_file.fileName());
        }
    }
        break;
    case FileSystemMenuManager::DirAct_AddNewDir:
    case FileSystemMenuManager::DirAct_AddNewFile:
    {
        // 创建文件或文件夹
        auto create = [&](FileSystemMenuManager::FileSystemAction act, const QString &path)->bool {
            bool rslt = false;
            switch (act) {
            case FileSystemMenuManager::DirAct_AddNewDir:
                rslt = FileSystemHandler::createDir(path);
                break;
            case FileSystemMenuManager::DirAct_AddNewFile:
                rslt = FileSystemHandler::createFile(path);
                break;
            default:
                break;
            }
            return rslt;
        };

        QString dir_path = file_item->data(0, Qt::UserRole).toString();
        QDir dir(dir_path);
        if (!dir.exists()) {
            // 新建文件时，当前临时item中保存的是所在文件夹
            delete file_item;
            file_item = Q_NULLPTR;
            break;
        }

        QString file_path = dir_path + "/" + file_item->getText();

        if (create(static_cast<FileSystemMenuManager::FileSystemAction>(m_current_action), file_path)) {
            file_item->setFileInfo(QFileInfo(file_path));

            if (file_item->type() == FileSystemItem::ItemType_Dir) {
                // 如果当前新建的item为文件夹，则添加空节点，使该节点可展开/折叠
                DirItem *dir_item = dynamic_cast<DirItem *>(file_item);
                if (dir_item) {
                    dir_item->addTempEmptyItem();
                }
            }
        }
        else {
            QMessageBox::critical(this, tr("Create"), tr("The file(\"%1\") created unsuccessfully !").arg(file_item->getText()),
                                  QMessageBox::Ok | QMessageBox::Ok);

            DirItem *dir_item = dynamic_cast<DirItem *>(item->parent());

            if (dir_item && dir_item->isEmpty()) {
                // 删除文件后，当前文件夹如果为空，则新增空节点，使当前节点为可展开状态
                dir_item->addTempEmptyItem();
            }

            delete file_item;
            file_item = Q_NULLPTR;
        }
    }
        break;
    default:
        break;
    }
}

void FileSystemTreeWidget::slotOnCustomContextMenuRequested(const QPoint &pos)
{
    FileSystemItem *item = dynamic_cast<FileSystemItem *>(this->itemAt(pos));
    if (!item) {
        return;
    }

    QFileInfo fi = item->getFileInfo();
    FileSystemMenuManager::FileSystemAction act = FileSystemMenuManager::FileSystem_None;
    if (fi.isRoot()) {
        act = FileSystemMenuManager::execRootMenu(item);
    }
    else if (fi.isDir()) {
        act = FileSystemMenuManager::execDirMenu(item);
    }
    else if (fi.isFile()) {
        act = FileSystemMenuManager::execFileMenu(item);
    }
    else {

    }

    this->doAction(item, act);
}

QTreeWidgetItem *FileSystemTreeWidget::getHeaderItem()
{
    QTreeWidgetItem *header_item = this->headerItem();
    if (!header_item) {
        header_item = new QTreeWidgetItem();
        this->setHeaderItem(header_item);
    }
    return header_item;
}

void FileSystemTreeWidget::doAction(FileSystemItem *item, int action)
{
    // 记录当前的action，用于新建等操作响应槽函数中判断
    m_current_action = action;

    switch (action) {
    case FileSystemMenuManager::FileAct_Open:
    {
        emit signalOpenFile(item->getFileInfo().absoluteFilePath());
    }
        break;
    case FileSystemMenuManager::FileAct_ShowInExplorer:
    case FileSystemMenuManager::DirAct_ShowInExplorer:
    {
        FileSystemHandler::showInExplorer(item->getFileInfo().absoluteFilePath());
    }
        break;
    case FileSystemMenuManager::FileAct_Rename:
    case FileSystemMenuManager::DirAct_Rename:
    {
        this->editItem(item);
    }
        break;
    case FileSystemMenuManager::FileAct_Remove:
    {
        QString path = item->getFileInfo().absoluteFilePath();
        QMessageBox::StandardButton btn = QMessageBox::question(this, tr("Remove"), tr("File to remove: \"%1\"?").arg(path),
                                                                QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok);
        if (btn == QMessageBox::Ok
                && FileSystemHandler::removeFile(item->getFileInfo().absoluteFilePath())) {

            DirItem *dir_item = dynamic_cast<DirItem *>(item->parent());

            if (dir_item && dir_item->isEmpty()) {
                // 删除文件后，当前文件夹如果为空，则新增空节点，使当前节点为可展开状态
                dir_item->addTempEmptyItem();
            }

            delete item;
            item = Q_NULLPTR;
        }
    }
        break;
    case FileSystemMenuManager::DirAct_AddNewFile:
    {
        this->expandItem(item);

        // 判断第一个子节点是否为临时空节点
        QTreeWidgetItem *child = item->child(0);
        if (child->type() == FileSystemItem::ItemType_Temp) {
            delete child;
            child = Q_NULLPTR;
        }

        FileItem *sub_item = new FileItem(m_provider);
        // 将父文件夹路径保存到临时节点中
        sub_item->setData(0, Qt::UserRole, item->getFileInfo().absoluteFilePath());
        item->addChild(sub_item);
        this->editItem(sub_item);
    }
        break;
    case FileSystemMenuManager::DirAct_AddNewDir:
    {
        this->expandItem(item);

        // 判断第一个子节点是否为临时空节点
        QTreeWidgetItem *child = item->child(0);
        if (child->type() == FileSystemItem::ItemType_Temp) {
            delete child;
            child = Q_NULLPTR;
        }

        DirItem *sub_item = new DirItem(m_provider, false);
        // 将父文件夹路径保存到临时节点中
        sub_item->setData(0, Qt::UserRole, item->getFileInfo().absoluteFilePath());
        item->addChild(sub_item);
        this->editItem(sub_item);
    }
        break;
    case FileSystemMenuManager::DirAct_Remove:
    {
        QString path = item->getFileInfo().absoluteFilePath();
        QMessageBox::StandardButton btn = QMessageBox::question(this, tr("Remove"), tr("Directory to remove: \"%1\"?").arg(path),
                                                                QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok);
        if (btn == QMessageBox::Ok
                && FileSystemHandler::removeDir(item->getFileInfo().absoluteFilePath())) {

            DirItem *dir_item = dynamic_cast<DirItem *>(item->parent());

            if (dir_item && dir_item->isEmpty()) {
                // 删除文件后，当前文件夹如果为空，则新增空节点，使当前节点为可展开状态
                dir_item->addTempEmptyItem();
            }

            delete item;
            item = Q_NULLPTR;
        }
    }
        break;
    case FileSystemMenuManager::RootAct_Expand:
    case FileSystemMenuManager::DirAct_Expand:
    {
        this->expandItem(item);
    }
        break;
    case FileSystemMenuManager::RootAct_Collapse:
    case FileSystemMenuManager::DirAct_Collapse:
    {
        this->collapseItem(item);
    }
        break;
    default:
        break;
    }
}

void FileSystemTreeWidget::addBegin()
{
    disconnect(this, SIGNAL(itemChanged(QTreeWidgetItem *, int)),
               this, SLOT(slotOnItemChanged(QTreeWidgetItem *, int)));
}

void FileSystemTreeWidget::addFinished()
{
    connect(this, SIGNAL(itemChanged(QTreeWidgetItem *, int)),
            this, SLOT(slotOnItemChanged(QTreeWidgetItem *, int)));
}

/**
 * @brief FileSystemDisplayProvider
 */
QPair<QString, QIcon> FileSystemDisplayProvider::getDisplayInfo(const QFileInfo &fi, int state) const
{
    QIcon icon;
    QString text;
    if (fi.isRoot()) {
        icon = GET_ICON("driver");
        text = fi.filePath();
    }
    else if (fi.isDir()) {
        icon = (state == 0) ? GET_ICON("folder") : GET_ICON("folder_open");
        text = fi.fileName();
    }
    else if (fi.isFile()) {
        icon = QFileIconProvider::icon(fi);
        text = fi.fileName();
    }
    else {

    }
    return QPair<QString, QIcon>(text, icon);
}

/**
 * @brief FileSystemMenuManager
 */
FileSystemMenuManager::FileSystemAction FileSystemMenuManager::execDirMenu(FileSystemItem *item)
{
    QMenu menu;

    QString dir_name = item->getFileInfo().fileName();

    QAction *act = Q_NULLPTR;

    if (item->isExpanded()) {
        act = menu.addAction(GET_ICON("collapse"), QObject::tr("Collapse \"%1\"").arg(dir_name));
        act->setData(static_cast<int>(DirAct_Collapse));
    }
    else {
        act = menu.addAction(GET_ICON("expand"), QObject::tr("Expand \"%1\"").arg(dir_name));
        act->setData(static_cast<int>(DirAct_Expand));
    }

    menu.addSeparator();

    act = menu.addAction(GET_ICON("open_file"), QObject::tr("New File..."));
    act->setData(static_cast<int>(DirAct_AddNewFile));

    act = menu.addAction(GET_ICON("open_folder"), QObject::tr("New Directory..."));
    act->setData(static_cast<int>(DirAct_AddNewDir));

    act = menu.addAction(GET_ICON("rename"), QObject::tr("Rename \"%1\"").arg(dir_name));
    act->setData(static_cast<int>(DirAct_Rename));

    act = menu.addAction(GET_ICON("remove"), QObject::tr("Remove \"%1\"").arg(dir_name));
    act->setData(static_cast<int>(DirAct_Remove));

    menu.addSeparator();

    act = menu.addAction(GET_ICON("show_in_explorer"), QObject::tr("Show \"%1\" in Explorer").arg(dir_name));
    act->setData(static_cast<int>(DirAct_ShowInExplorer));

    act = menu.exec(QCursor::pos());

    FileSystemAction act_type = FileSystem_None;
    if (act) {
        act_type = static_cast<FileSystemAction>(act->data().toInt());
    }

    return act_type;
}

FileSystemMenuManager::FileSystemAction FileSystemMenuManager::execFileMenu(FileSystemItem *item)
{
    QMenu menu;

    QString file_name = item->getFileInfo().fileName();

    QAction *act = menu.addAction(GET_ICON("open_file"), QObject::tr("Open \"%1\"").arg(file_name));
    act->setData(static_cast<int>(FileAct_Open));

    menu.addSeparator();

    act = menu.addAction(GET_ICON("rename"), QObject::tr("Rename \"%1\"").arg(file_name));
    act->setData(static_cast<int>(FileAct_Rename));

    act = menu.addAction(GET_ICON("remove"), QObject::tr("Remove \"%1\"").arg(file_name));
    act->setData(static_cast<int>(FileAct_Remove));

    menu.addSeparator();

    act = menu.addAction(GET_ICON("folder_open"), QObject::tr("Show \"%1\" in Explorer").arg(file_name));
    act->setData(static_cast<int>(FileAct_ShowInExplorer));

    act = menu.exec(QCursor::pos());

    FileSystemAction act_type = FileSystem_None;
    if (act) {
        act_type = static_cast<FileSystemAction>(act->data().toInt());
    }

    return act_type;
}

FileSystemMenuManager::FileSystemAction FileSystemMenuManager::execRootMenu(FileSystemItem *item)
{
    QMenu menu;

    QString driver_name = item->getFileInfo().filePath();

    QAction *act = menu.addAction(GET_ICON("expand"), QObject::tr("Expand \"%1\"").arg(driver_name));
    act->setData(static_cast<int>(RootAct_Expand));

    act = menu.addAction(GET_ICON("collapse"), QObject::tr("Collapse \"%1\"").arg(driver_name));
    act->setData(static_cast<int>(RootAct_Collapse));

    act = menu.exec(QCursor::pos());

    FileSystemAction act_type = FileSystem_None;
    if (act) {
        act_type = static_cast<FileSystemAction>(act->data().toInt());
    }

    return act_type;
}

} // namespace Domy
